﻿/*
 * 作者：新生命开发团队（http://www.newlifex.com/）
 * 
 * 版权：版权所有 (C) 新生命开发团队 2002-2014
 * 
 * 修改：海洋饼干（cuteant@outlook.com）
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using CuteAnt.Reflection;
using CuteAnt.Serialization;

namespace CuteAnt.Xml
{
	/// <summary>Xml读取器</summary>
	public class XmlReaderX : TextReaderBase<XmlReaderWriterSettings>
	{
		#region 属性

		private XmlReader _Reader;

		/// <summary>读取器</summary>
		public XmlReader Reader
		{
			get
			{
				if (_Reader == null)
				{
					//var settings = new XmlReaderSettings();
					//settings.IgnoreWhitespace = true;
					//settings.IgnoreComments = true;
					//_Reader = XmlReader.Create(Stream, settings);
					// 使用XmlReader.Create会导致读取字符串时\r\n变成\n
					var xr = new XmlTextReader(Stream);
					xr.WhitespaceHandling = WhitespaceHandling.Significant;
					_Reader = xr;
				}
				return _Reader;
			}
			set
			{
				_Reader = value;

				var xr = _Reader as XmlTextReader;
				if (xr != null && Settings.Encoding != xr.Encoding) Settings.Encoding = xr.Encoding;
			}
		}

		/// <summary>数据流。更改数据流后，重置Reader为空，以使用新的数据流</summary>
		public override Stream Stream
		{
			get { return base.Stream; }
			set
			{
				if (base.Stream != value) _Reader = null;
				base.Stream = value;
			}
		}

		/// <summary>获取一个值，该值表示当前的流位置是否在流的末尾。</summary>
		/// <returns>如果当前的流位置在流的末尾，则为 true；否则为 false。</returns>
		public override bool EndOfStream
		{
			get
			{
				var r = Reader;
				if (r != null) return r.EOF;

				var s = Stream;
				if (s != null) return s.Position == s.Length;

				return false;
			}
		}

		/// <summary>初始化</summary>
		public XmlReaderX()
		{
		}

		/// <summary>使用xml字符串初始化</summary>
		/// <param name="xml"></param>
		public XmlReaderX(String xml)
		{
			Stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
		}

		private String _RootName;

		/// <summary>根元素名</summary>
		public String RootName { get { return _RootName; } set { _RootName = value; } }

		private String _Lengths;

		/// <summary>多维数组长度</summary>
		public String Lengths { get { return _Lengths; } set { _Lengths = value; } }

		#endregion

		#region 基础元数据

		/// <summary>从当前流中读取一个字符串。字符串有长度前缀，一次 7 位地被编码为整数。</summary>
		/// <returns></returns>
		public override String ReadString()
		{
			var isElement = Reader.NodeType == XmlNodeType.Element;
			if (isElement)
			{
				if (SkipEmpty()) return null;
				Reader.ReadStartElement();
			}

			String str;

			// 字符串可能以字符数组的形式存在
			if (Reader.NodeType == XmlNodeType.Element && Reader.Name.EqualIgnoreCase("char"))
			{
				var sb = new StringBuilder();
				while (Reader.NodeType == XmlNodeType.Element && Reader.Name.EqualIgnoreCase("char"))
				{
					Reader.ReadStartElement();
					var s = Reader.ReadContentAsString();
					sb.Append((Char)Int32.Parse(s));
					Reader.ReadEndElement();
				}
				str = sb.ToString();
			}
			else
				str = Reader.ReadContentAsString();

			if (isElement && Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

			WriteLog(1, "ReadString", str);
			return str;
		}

		/// <summary>从当前流中读取 count 个字符，以字符数组的形式返回数据，并根据所使用的 Encoding 和从流中读取的特定字符，提升当前位置。</summary>
		/// <param name="count">要读取的字符数。</param>
		/// <returns></returns>
		public override char[] ReadChars(int count)
		{
			var str = ReadString();
			if (str == null) return null;

			return str.ToCharArray();
		}

		/// <summary>从当前流中读取下一个字符，并根据所使用的 Encoding 和从流中读取的特定字符，提升流的当前位置。</summary>
		/// <returns></returns>
		public override Char ReadChar()
		{
			return (Char)Byte.Parse(ReadString());
		}

		/// <summary>读取一个时间日期</summary>
		/// <returns></returns>
		public override DateTime ReadDateTime()
		{
			return XmlConvert.ToDateTime(ReadString(), Settings.DateTimeMode);
		}

		/// <summary>尝试读取值类型数据，返回是否读取成功</summary>
		/// <param name="type">要读取的对象类型</param>
		/// <param name="value">要读取的对象</param>
		/// <returns></returns>
		public override bool ReadValue(Type type, ref object value)
		{
			if (type == null)
			{
				if (value == null) return false;
				type = value.GetType();
			}

			var code = Type.GetTypeCode(type);

			// XmlConvert特殊处理时间，输出Char时按字符输出，不同于Xml序列化的数字，所以忽略
			if (code != TypeCode.Char && code != TypeCode.String && code != TypeCode.DateTime)
			{
				// XmlConvert也支持这三种值类型
				if (type.CanXmlConvert())
				{
					var xml = ReadString();
					value = XmlHelper.XmlConvertFromString(type, xml);
					return true;
				}
			}

			return base.ReadValue(type, ref value);
		}

		#endregion

		#region 扩展类型

		/// <summary>读对象类型</summary>
		/// <returns></returns>
		protected override Type OnReadType()
		{
			if (Reader.MoveToAttribute("Type"))
			{
				var t = base.OnReadType();
				if (Reader.NodeType == XmlNodeType.Attribute) Reader.MoveToElement();
				return t;
			}

			return base.OnReadType();
		}

		#endregion

		#region 字典

		/// <summary>尝试读取字典类型对象</summary>
		/// <param name="type">类型</param>
		/// <param name="value">对象</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否读取成功</returns>
		public override bool ReadDictionary(Type type, ref object value, ReadObjectCallback callback)
		{
			if (SkipEmpty()) return true;

			return base.ReadDictionary(type, ref value, callback);
		}

		/// <summary>读取字典项</summary>
		/// <param name="keyType">键类型</param>
		/// <param name="valueType">值类型</param>
		/// <param name="value">字典项</param>
		/// <param name="index">元素序号</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否读取成功</returns>
		protected override bool ReadDictionaryEntry(Type keyType, Type valueType, ref DictionaryEntry value, Int32 index, ReadObjectCallback callback)
		{
			if (Reader.NodeType == XmlNodeType.EndElement) return false;

			Object key = null;
			Object val = null;

			// <Item>
			Reader.ReadStartElement();

			// <Key>
			Reader.ReadStartElement();
			if (!ReadObject(keyType, ref key)) return false;

			// </Key>
			if (Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

			// <Value>
			if (!ReadObject(valueType, ref val)) return false;

			// </Value>

			value.Key = key;
			value.Value = val;

			// </Item>
			if (Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

			return true;
		}

		#endregion

		#region 枚举

		/// <summary>尝试读取枚举</summary>
		/// <remarks>重点和难点在于如果得知枚举元素类型，这里假设所有元素类型一致，否则实在无法处理</remarks>
		/// <param name="type">要读取的对象类型</param>
		/// <param name="value">要读取的对象</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否读取成功</returns>
		public override bool ReadEnumerable(Type type, ref object value, ReadObjectCallback callback)
		{
			Type t = type.GetElementType();

			#region 锯齿二维数组处理

			Int32 length = 1;
			while (typeof(IEnumerable).IsAssignableFrom(t))
			{
				length++;
				t = t.GetElementType();
			}

			if (length > 1)
			{
				Array array = type.CreateInstance(length) as Array;
				t = type.GetElementType();
				for (int j = 0; j < length - 1; j++)
				{
					//开始循环之前已赋值，所以第一次循环时跳过
					if (j > 0) t = t.GetElementType();

					for (int i = 0; i < array.Length; i++)
					{
						if (value != null) value = null;
						if (base.ReadEnumerable(t, ref value, callback) && value != null)
						{
							array.SetValue(value, i);
						}
					}
				}
				if (array != null && array.Length > 0) value = array;
				return true;
			}

			#endregion

			return base.ReadEnumerable(type, ref value, callback);
		}

		/// <summary>读取枚举项</summary>
		/// <param name="type">类型</param>
		/// <param name="value">数值</param>
		/// <param name="index">元素序号</param>
		/// <param name="callback">处理元素的方法</param>
		/// <returns></returns>
		protected override bool ReadItem(Type type, ref object value, Int32 index, ReadObjectCallback callback)
		{
			String name = type.GetCustomAttributeValue<XmlRootAttribute, String>(true);
			if (name.IsNullOrWhiteSpace() && type != null) name = type.Name;

			if (Reader.NodeType == XmlNodeType.EndElement || Reader.Name != name) return false;

			Boolean rs = base.ReadItem(type, ref value, index, callback);

			if (Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

			return rs;
		}

		#endregion

		#region 读取对象

		/// <summary>尝试读取目标对象指定成员的值，通过委托方法递归处理成员</summary>
		/// <param name="type">要读取的对象类型</param>
		/// <param name="value">要读取的对象</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否读取成功</returns>
		protected override bool OnReadObject(Type type, ref object value, ReadObjectCallback callback)
		{
			if (Depth > 1) return base.OnReadObject(type, ref value, callback);

			while (Reader.NodeType != XmlNodeType.Element) { if (!Reader.Read())return false; }
			if (RootName.IsNullOrWhiteSpace()) RootName = Reader.Name;

			return base.OnReadObject(type, ref value, callback);
		}

		/// <summary>尝试读取引用对象</summary>
		/// <param name="type">要读取的对象类型</param>
		/// <param name="value">要读取的对象</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否读取成功</returns>
		protected override bool ReadRefObject(Type type, ref object value, ReadObjectCallback callback)
		{
			Boolean isElement = Reader.NodeType == XmlNodeType.Element;
			Boolean isAtt = Settings.MemberAsAttribute && XmlWriterX.IsAttributeType(type);
			if (isElement && !isAtt)
			{
				if (SkipEmpty()) return true;
				if (Reader.MoveToAttribute("Lengths"))
				{
					WriteLog("ReadLengths");
					Lengths = ReadString();
					if (Reader.NodeType == XmlNodeType.Attribute) Reader.MoveToElement();
				}
				Reader.ReadStartElement();
			}

			Boolean b = base.ReadRefObject(type, ref value, callback);

			if (isElement && !isAtt && Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();

			return b;
		}

		/// <summary>读取成员之前获取要读取的成员，默认是index处的成员，实现者可以重载，改变当前要读取的成员，如果当前成员不在数组里面，则实现者自己跳到下一个可读成员。</summary>
		/// <param name="type">要读取的对象类型</param>
		/// <param name="value">要读取的对象</param>
		/// <param name="members">可匹配成员数组</param>
		/// <param name="index">索引</param>
		/// <returns></returns>
		protected override IObjectMemberInfo GetMemberBeforeRead(Type type, object value, IObjectMemberInfo[] members, int index)
		{
			String name = String.Empty;

			while (Reader.NodeType != XmlNodeType.None && Reader.IsStartElement())
			{
				name = Reader.Name;

				IObjectMemberInfo member = GetMemberByName(members, name);
				if (member != null) return member;

				if (SkipEmpty()) continue;

				Reader.ReadStartElement();

				if (!SkipEmpty()) Reader.Read();

				if (Reader.NodeType == XmlNodeType.EndElement) Reader.ReadEndElement();
			}

			return null;
		}

		/// <summary>读取对象成员</summary>
		/// <param name="type">要读取的对象类型</param>
		/// <param name="value">要读取的对象</param>
		/// <param name="member">成员</param>
		/// <param name="index">成员索引</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否读取成功</returns>
		protected override bool OnReadMember(Type type, ref object value, IObjectMemberInfo member, Int32 index, ReadObjectCallback callback)
		{
			Boolean isAtt = Settings.MemberAsAttribute && XmlWriterX.IsAttributeType(member.Type);
			if (isAtt) Reader.MoveToAttribute(member.Name);

			return base.OnReadMember(type, ref value, member, index, callback);
		}

		/// <summary>读取对象引用计数</summary>
		/// <returns></returns>
		protected override int OnReadObjRefIndex()
		{
			if (Reader.MoveToAttribute("ObjRef"))
			{
				Int32 rs = ReadInt32();

				// 从特性移到元素，方便后续读取操作，如果后续还需要读取特性，则应该自己移到特性处
				if (Reader.NodeType == XmlNodeType.Attribute) Reader.MoveToElement();

				// 跳过空元素，可能该元素已经读取了对象引用
				SkipEmpty();

				return rs;
			}

			return -1;
		}

		#endregion

		#region 未知对象

		/// <summary>读取未知对象（其它所有方法都无法识别的对象），采用BinaryFormatter或者XmlSerialization</summary>
		/// <param name="type">要读取的对象类型</param>
		/// <param name="value">要读取的对象</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否读取成功</returns>
		public override bool ReadUnKnown(Type type, ref object value, ReadObjectCallback callback)
		{
			try
			{
				WriteLog("XmlSerializer", type.Name);

				//XmlSerializer serializer = new XmlSerializer(type);
				//String str = Reader.ReadString();
				//if (!str.IsNullOrWhiteSpace())
				//{
				//    MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(str));
				//    value = serializer.Deserialize(ms);
				//}
				value = Reader.ReadString().ToXmlEntity(type);
				return true;
			}
			catch
			{
				//只能处理公共类型,Type因其保护级别而不可访问。
			}
			return base.ReadUnKnown(type, ref value, callback);
		}

		#endregion

		#region 方法

		/// <summary>当前节点是否空。如果是空节点，则读一次，让指针移到下一个元素</summary>
		private Boolean SkipEmpty()
		{
			// 空元素直接返回
			if (Reader.IsEmptyElement)
			{
				// 读一次，把指针移到下一个元素上
				Reader.Read();
				return true;
			}

			return false;
		}

		/// <summary>读取多维数组相关参数</summary>
		/// <returns></returns>
		protected override string ReadLengths()
		{
			return Lengths;
		}

		/// <summary>备份当前环境，用于临时切换数据流等</summary>
		/// <returns>本次备份项集合</returns>
		public override IDictionary<String, Object> Backup()
		{
			var dic = base.Backup();
			dic["Reader"] = Reader;

			return dic;
		}

		/// <summary>恢复最近一次备份</summary>
		/// <returns>本次还原项集合</returns>
		public override IDictionary<String, Object> Restore()
		{
			var dic = base.Restore();
			Reader = dic["Reader"] as XmlReader;

			return dic;
		}

		#endregion

		#region 序列化接口

		/// <summary>读取实现了可序列化接口的对象</summary>
		/// <param name="type">要读取的对象类型</param>
		/// <param name="value">要读取的对象</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否读取成功</returns>
		public override bool ReadSerializable(Type type, ref object value, ReadObjectCallback callback)
		{
			if (!typeof(IXmlSerializable).IsAssignableFrom(type))
				return base.ReadSerializable(type, ref value, callback);

			if (value == null) value = type.CreateInstance();
			((IXmlSerializable)value).ReadXml(Reader);
			return true;
		}

		#endregion
	}
}